#ifndef __NANO_DB_H__
#define __NANO_DB_H__
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/xpointer.h>
#include <libxml/xmlsave.h> 
#include <stdint.h>

#ifdef __cplusplus
extern "C"{
#endif

typedef xmlNodePtr ndb_elem_ptr;
typedef xmlNodeSetPtr ndb_elset_ptr;

struct ndb_ns_t
{
    const char* name;
    const char* href;
};

struct ndb_t
{
    const char* fname;
    xmlDocPtr xmldoc;
    xmlNodePtr root;
};

struct ndb_ctx_t
{
    const char* ns;
    xmlXPathContextPtr xpath_ctx;
};

struct ndb_tabset_t
{
    xmlXPathObjectPtr xpobj;
    int tbl_nr;
};

struct ndb_cursor_t
{
    xmlNodePtr node;
    xmlNodePtr prev;
    xmlNodePtr anchor;
    int tbl_idx;
};

enum ndb_vtype
{
    C_STR    = 0,
    C_UINT32 = 1,
    C_UINT16 = 2,
    C_SINT32 = 3,
    C_SINT16 = 4,
    C_UINT8  = 5,
    C_SINT8  = 6,
    C_FLOAT  = 7
};

struct ndb_value_t
{
    enum ndb_vtype type;

    union {
        char* s;
        uint32_t u32;
        uint16_t u16;
        int32_t  s32;
        int16_t  s16;
        uint8_t  u8;
        int8_t   s8;
        float    f;
    } val;
};

/*
 * Per-defined XML namespace
 *
 */
extern struct ndb_ns_t skynetworks_ns;
extern struct ndb_ns_t router_ns;
extern struct ndb_ns_t equipment_ns;
extern struct ndb_ns_t interface_ns;
extern struct ndb_ns_t system_ns;
extern struct ndb_ns_t traffic_man_ns;
extern struct ndb_ns_t services_ns;

#define skynetworks__ns        (&skynetworks_ns)
#define router__ns      (&router_ns)
#define equipment__ns   (&equipment_ns)
#define interface__ns   (&interface_ns)
#define system__ns      (&system_ns)
#define traffic_man__ns (&traffic_man_ns)
#define services__ns    (&services_ns)


/*
 * Init a ndb_t object, load XML data file
 *
 * return: 0 - sucess, -1 - failed
 */
int ndb_create(struct ndb_t* db, const char* file);

/*
 * Save config data in ndb object to as file `file'
 *
 * return: 0 - sucess, -1 - failed
 */
int ndb_save_as(struct ndb_t* db, const char* file);

int ndb_save(struct ndb_t* db);
/*
 * Destory a ndb_t object
 *
 * return: 0 - sucess, -1 - failed
 */
int ndb_destory(struct ndb_t* db);

/*
 * Create a query context object
 *
 * return: 0 - sucess, -1 - failed
 */
int ndb_create_context(struct ndb_t* db, struct ndb_ns_t* ns, struct ndb_ctx_t* ctx);

int ndb_register_namespaces(struct ndb_ctx_t* ctx, struct ndb_ns_t* nss[], int num);
/*
 * Destory a query context object
 *
 * return: 0 - sucess, -1 - failed
 */
int ndb_destory_context(struct ndb_ctx_t* ctx);

/*
 * Execute a XPath query
 *
 * db:
 *    ndb object which has created.
 *
 * ctx:
 *    ndb query context which has created.
 *
 * xpath:
 *
 * set:
 * Note:each node maybe a root of a subtree in returned table
 * return: n of nodes - sucess, -1 - failed
 */
int ndb_query(struct ndb_t* db, struct ndb_ctx_t* ctx, const char* xpath, struct ndb_tabset_t* set);

/*
 * get number of tables in table set
 *
 * return:  
 */
int ndb_get_tab_nr(struct ndb_tabset_t* set);

ndb_elem_ptr ndb_get_first(struct ndb_tabset_t* set);

ndb_elem_ptr ndb_add_child(ndb_elem_ptr p, struct ndb_ns_t* ns, const char* name, const char* val);

ndb_elem_ptr ndb_add_sibling(ndb_elem_ptr p, struct ndb_ns_t* ns, const char* name, const char* val);

/*
 * Set property value specific by `propname' of Node 
 *
 */
int ndb_set_prop(ndb_elem_ptr elem, const char* propname, const char* popval);

/*
 * Get property value specific by `propname' of Node 
 *
 */
const char* ndb_get_prop(ndb_elem_ptr elem, const char* propname);

/*
 * test if node has property specific by `propname' of Node 
 *
 */
int ndb_has_prop(ndb_elem_ptr elem, const char* propname);

int ndb_has_prop_and_value(ndb_elem_ptr elem, const char* propname, const char* popval);

/*
 * set/get (first) node's name/value. 
 * 
 * return:  
 */
const char* ndb_get_name(ndb_elem_ptr elem);

const char* ndb_get_value_as_str(ndb_elem_ptr elem);

int32_t ndb_get_value_as_int32(ndb_elem_ptr elem);
uint32_t ndb_get_value_as_u32(ndb_elem_ptr elem);
float ndb_get_value_as_float(ndb_elem_ptr elem);
uint16_t ndb_get_value_as_u16(ndb_elem_ptr elem);
int16_t ndb_get_value_as_int16(ndb_elem_ptr elem);
uint8_t ndb_get_value_as_u8(ndb_elem_ptr elem);
int8_t ndb_get_value_as_int8(ndb_elem_ptr elem);
int ndb_get_value(ndb_elem_ptr elem, struct ndb_value_t* value);

void ndb_set_str(ndb_elem_ptr elem, const char* value);
void ndb_set_int32(ndb_elem_ptr elem, int val);
void ndb_set_u32(ndb_elem_ptr elem, uint32_t val);
void ndb_set_u16(ndb_elem_ptr elem, uint16_t val);
void ndb_set_int16(ndb_elem_ptr elem, int16_t val);
void ndb_set_u8(ndb_elem_ptr elem, uint8_t val);
void ndb_set_int8(ndb_elem_ptr elem, int8_t val);
void ndb_set_float(ndb_elem_ptr elem, float val);
int ndb_set_value(ndb_elem_ptr elem, struct ndb_value_t* value);

const char* ndb_path(struct ndb_tabset_t* set);

/*
 * get nodeset pointer. 
 * 
 * return:  
 */
ndb_elset_ptr get_elem_set(struct ndb_tabset_t* set);

#define for_each_subtree(elset, elp, i) \
    for (elp = elset->nodeTab[0], i = 0; i <  elset->nodeNr; elp = elset->nodeTab[++i])

#define for_each_children(rootp, elp) \
    for (elp = rootp->children; elp != NULL; elp = elp->next)

#define CONTINUE(elp) \
    if (xmlNodeIsText(elp)) \
        continue

#define NDB_SET_STR(_v, str) \
    do { \
        struct ndb_value_t* __val = (_v); \
        __val->type = C_STR; \
        __val->val.s = str; \
    } while (0)

#define NDB_SET_U32(_v, ui) \
    do { \
        struct ndb_value_t* __val = (_v); \
        __val->type = C_UINT32; \
        __val->val.u32 = ui; \
    } while (0)

#define NDB_SET_S32(_v, si) \
    do { \
        struct ndb_value_t* __val = (_v); \
        __val->type = C_SINT32; \
        __val->val.s32 = si; \
    } while (0)

#define NDB_SET_U8(_v, ui) \
    do { \
        struct ndb_value_t* __val = (_v); \
        __val->type = C_UINT8; \
        __val->val.u8 = ui; \
    } while (0)

#define NDB_SET_S8(_v, si) \
    do { \
        struct ndb_value_t* __val = (_v); \
        __val->type = C_SINT8; \
        __val->val.s8 = si; \
    } while (0)

#define NDB_SET_FLOAT(_v, f) \
    do { \
        struct ndb_value_t* __val = (_v); \
        __val->type = C_SINT32; \
        __val->val.f = f; \
    } while (0)

#define NDB_TYPE(_v, T) \
    ((_v)->type = T, (_v))

#define NDB_GET_STR(_v) (const char*)(_v)->val.s
#define NDB_GET_U32(_v) (_v)->val.u32
#define NDB_GET_S32(_v) (_v)->val.s32
#define NDB_GET_U8(_v) (_v)->val.u8
#define NDB_GET_S8(_v) (_v)->val.s8
#define NDB_GET_FLOAT(_v) (_v)->val.f

/*
 * compare elem's name and param 'name', 
 * 
 * if yes, return 0; no return -1  
 */
int ndb_compare(ndb_elem_ptr elem, const char* name);

/*
 * compare elem's name and param 'name', 
 * 
 * if yes, set node value, return 0; if no return -1  
 */
int ndb_compare_and_set(ndb_elem_ptr elem, const char* name, struct ndb_value_t* value);

/*
 * compare elem's name and param 'name', 
 * 
 * if yes, get node value, return 0; if no return -1  
 */
int ndb_compare_and_get(ndb_elem_ptr elem, const char* name, struct ndb_value_t* value);

int ndb_test_prop_and_set(ndb_elem_ptr elem, const char* propname, const char* propval);

const char* ndb_test_prop_and_get(ndb_elem_ptr elem, const char* propname);

/*
 * Attach cursor to root node of first table in table set.
 *
 * return: 0 - sucess, -1 - failed
 */
int ndbc_attach(struct ndb_cursor_t* c, struct ndb_tabset_t* set);

/*
 *  Move cursor to root node of next table in table set
 *
 *  return: 0 - sucess, -1 - failed
 */
int ndbc_next_table(struct ndb_cursor_t* c, struct ndb_tabset_t* set);

/*
 *  Test if there has more table in table set
 *
 *  return: 0 - sucess, non-zero - failed
 */
int ndbc_more_table(struct ndb_cursor_t* c, struct ndb_tabset_t* set);

/*
 * Move cursor to next brotherhood node 
 *
 * return: 0 - sucess, -1 - failed
 */
int ndbc_move(struct ndb_cursor_t* c);

/*
 * Move cursor down to first children node of the node pointed by cursor
 *
 * return: 0 - sucess, -1 - failed
 */
int ndbc_down(struct ndb_cursor_t* c);

/*
 * Move cursor back to parent node of the node pointed by cursor
 */
int ndbc_up(struct ndb_cursor_t* c);

/*
 * Get Node name pointed by cursor
 *
 */
const char* ndbc_get_name(struct ndb_cursor_t* c);

/*
 * Check if Node has empty content pointed by cursor
 *
 */
int ndbc_empty(struct ndb_cursor_t* c);

/*
 * Get xpath value of Node pointed by curso
 *
 */
const char* ndbc_path(struct ndb_cursor_t* c);

/*
 * Get property value specific by `propname' of Node pointed by cursor
 *
 */
const char* ndbc_get_prop(struct ndb_cursor_t* c, const char* propname);

/*
 * Set property value specific by `propname' of Node pointed by cursor
 *
 *  return: 0 - sucess, -1 - failed
 */
int ndbc_set_prop(struct ndb_cursor_t* c, const char* propname, const char* popval);

/*
 * Test if has property specific by `propname' of Node pointed by cursor
 *
 *  return: 0 - sucess, -1 - failed
 */
int ndbc_has_prop(struct ndb_cursor_t* c, const char* propname);

/*
 * Get Node value 
 *
 */
const char* ndbc_get_value_as_str(struct ndb_cursor_t* c);
#define ndbc_get_as_str ndbc_get_value_as_str

int ndbc_get_value_as_int32(struct ndb_cursor_t* c);
unsigned int ndbc_get_value_as_u32(struct ndb_cursor_t* c);
float ndbc_get_value_as_float(struct ndb_cursor_t* c);
uint16_t ndbc_get_value_as_u16(struct ndb_cursor_t* c);
int16_t ndbc_get_value_as_int16(struct ndb_cursor_t* c);
uint8_t ndbc_get_value_as_u8(struct ndb_cursor_t* c);
int8_t ndbc_get_value_as_int8(struct ndb_cursor_t* c);

int ndbc_get_value(struct ndb_cursor_t* c, struct ndb_value_t* value);


/*
 * Set Node value 
 *
 */
void ndbc_set_str(struct ndb_cursor_t* c, const char* value);

void ndbc_set_int32(struct ndb_cursor_t* c, int val);
void ndbc_set_u32(struct ndb_cursor_t* c, unsigned int val);
void ndbc_set_u16(struct ndb_cursor_t* , uint16_t val);
void ndbc_set_int16(struct ndb_cursor_t* , int16_t val);
void ndbc_set_u8(struct ndb_cursor_t* , uint8_t val);
void ndbc_set_int8(struct ndb_cursor_t* , int8_t val);
void ndbc_set_float(struct ndb_cursor_t* c, float val);

int ndbc_set_value(struct ndb_cursor_t* c, struct ndb_value_t* value);

void* ndb_get_container(ndb_elem_ptr container, void* buf);

/*
 * Iterates each table in ndb_tabset_t. 
 *
 */
#define for_each_table(TS, C)        \
    for (ndbc_attach(C, TS);         \
         ndbc_more_table(C, TS);     \
         ndbc_next_table(C, TS)) 

#ifdef __cplusplus
}
#endif

#endif /*__NANO_DB_H__*/

